下面的c语言程序示例展示了函数, 参数, 本地变量和局部变量(函数简短注释)
/* An example C program with local and global variables */
#include <stdio.h>
int max(int n1, int n2); /* function prototypes */
int change(int amt);
int g_x; /* global variable: declared outside function bodies */
int main(void) {
int x, result; /* local variables: declared inside function bodies */
printf("Enter a value: ");
scanf("%d", &x);
g_x = 10; /* global variables can be accessed in any function */
result = max(g_x, x);
printf("%d is the largest of %d and %d\n", result, g_x, x);
result = change(10);
printf("g_x's value was %d and now is %d\n", result, g_x);
return 0;
}
int max(int n1, int n2) { /* function with two parameters */
int val; /* local variable */
val = n1;
if ( n2 > n1 ) {
val = n2;
}
return val;
}
int change(int amt) {
int val;
val = g_x; /* global variables can be accessed in any function */
g_x += amt;
return val;
}
这个例子展示了程序变量的不同作用范围. 变量的作用范围由它们的定义决定.换句话说, 作用范围是由代码块中的变量及其关联使用的程序内存决定.
在函数外面定义的变量是全局变量. 全局变量永久有效, 在程序的任何地方都可以被访问, 因为它们被放在特别的内存区域. 每一个全局变量必须拥有唯一的名字 ---- 在整个程序运行期间这个唯一的名字代表对一个指定存储的标识符.
局部变量和参数的作用域在函数定义的范围内. 举个例子, amt
参数的作用域在 change
函数内. 这意味着只有 change
函数内的语句才能使用 amt
参数, 并且 amt
参数也会在随着每一个 change
函数实例执行时(当函数被被调用时, 会在栈上分配空间)分配一块内存. 当函数被调用执行时, 在栈上分配参数值的空间, 当函数返回后, 参数值的空间被释放. 函数的每一次调用都会给自己参数和局部变量分配空间. 因此, 对于递归函数的运行, 每一次递归调用都会在栈上申请包含参数和局部变量的函数空间.
因为参数和局部变量的作用域在函数定义的内部, 不同的函数可以使用相同名字来代表局部变量和函数参数. 举个例子, change
和 max
函数有一样名字的局部变量 val
. max
函数中的 val
变量不会改变 change
函数内部的局部变量 val
, 它们的作用域都在函数内.
虽然有时可能会使用c语言的全局变量, 我们强烈建议尽可能避免使用全局变量. 使用局部变量和参数可以让代码更加模块化, 更通用, 更易于调试. 同时, 因为在函数调用时分配函数参数和局部变量空间, 这种按需分配可以让程序空间利用更加高效.
当启动一个新的程序, 操作系统会分配新的程序地址空间. 一个程序的地址空间(内存空间)代表着执行过程中所有内容存储位置, 包括存储所有的指令和数据. 程序空间可以由一系列的可寻址的字节数组组成; 程序空间中的每个使用的地址存储全部和部分的指令和数据(一些程序执行时需要的附加状态).
程序内存空间会分成几个部分, 每个部分用于在进程的地址空间中存储不同类型的代码状态实体. Figure 1对程序内存空间的各个组成部分做了说明.
Figure 1. 程序内存地址空间布局
程序内存布局的顶部(内存地址从上到下增长)保留给操作系统使用, 剩下的部分给程序使用. 程序的 code (代码段)用来存储指令. 举个例子, 上面示例程序代码段存储 main
, max
, change
这些函数指令.
局部变量和参数驻留在 stack(栈: 先进后出的数据存储容器)中. 随着函数的调用和返回, 堆栈空间的大小随着程序的执行而增长和收缩. 内存的堆栈的新增部分通常分配在内存底部附近(从高内存地址往低内存地址增长), 这可以为栈的改变留出空间. 当函数被调用(函数被调用时会在栈上分配对应的栈帧(stack frame))时, 局部变量和参数才会在栈上分配空间.
全局变量存储在_data_(数据段). 不像栈那样, 数据段不会增长会缩小 — 全局变量的存储空间在程序的整个运行过程中持续存在.
最后, heap (堆空间)是程序地址空间中与动态内存分配相关的部分. 堆空间一般远离栈空间, 并且堆空间随着程序的运行会进行更多的动态内存分配, 增长方向是从低内存地址往高内存地址方向增长. 这和栈空间增长方向相反.